package cn.yh.nft.turbo.user.domain.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.yh.nft.turbo.api.user.constant.UserOperateTypeEnum;
import cn.yh.nft.turbo.api.user.constant.UserStateEnum;
import cn.yh.nft.turbo.api.user.request.UserActiveRequest;
import cn.yh.nft.turbo.api.user.request.UserAuthRequest;
import cn.yh.nft.turbo.api.user.request.UserModifyRequest;
import cn.yh.nft.turbo.api.user.response.UserOperatorResponse;
import cn.yh.nft.turbo.api.user.response.data.InviteRankInfo;
import cn.yh.nft.turbo.base.exception.BizException;
import cn.yh.nft.turbo.base.exception.RepoErrorCode;
import cn.yh.nft.turbo.base.response.PageResponse;
import cn.yh.nft.turbo.lock.DistributeLock;
import cn.yh.nft.turbo.user.domain.entity.User;
import cn.yh.nft.turbo.user.domain.entity.convertor.UserConvertor;
import cn.yh.nft.turbo.user.domain.service.AuthService;
import cn.yh.nft.turbo.user.domain.service.UserCacheDelayDeleteService;
import cn.yh.nft.turbo.user.infrastructure.exception.UserErrorCode;
import cn.yh.nft.turbo.user.infrastructure.exception.UserException;
import cn.yh.nft.turbo.user.infrastructure.mapper.UserMapper;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.CacheManager;
import com.alicp.jetcache.anno.CacheInvalidate;
import com.alicp.jetcache.anno.CacheRefresh;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.alicp.jetcache.template.QuickConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.PostConstruct;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.client.protocol.ScoredEntry;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.hutool.core.lang.Assert;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Time;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static cn.yh.nft.turbo.user.infrastructure.exception.UserErrorCode.*;

/**
 * @ClassName: UserService
 * @Author: 草莓爱上啵啵
 *
 *  用户服务
 */
@Service
public class UserService extends ServiceImpl<UserMapper, User> implements InitializingBean {

    /**
     * 用户昵称初始前缀
     */
    private static final String DEFAULT_NICK_NAME_PREFIX = "藏家_";

    /**
     * 用户初始密码
     */
    private static final String DEFAULT_USER_PASSWORD = "123456";

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserOperateStreamService userOperateStreamService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private AuthService authService;

    @Autowired
    private UserCacheDelayDeleteService userCacheDelayDeleteService;

    /**
     * 用户昵称布隆过滤器
     */
    private RBloomFilter<String> nickNameBloomFilter;

    /**
     * 用户邀请码布隆过滤器
     */
    private RBloomFilter<String> inviteCodeBloomFilter;

    private RScoredSortedSet<String> inviteRank;

    @Autowired
    private CacheManager cacheManager;

    private Cache<String, User> idUserCache;

    /**
     * 表示该方法在Bean的依赖注入完成后执行
     * 用于执行初始化逻辑
     */
    @PostConstruct
    public void init(){
        // 1. 创建一个QuickConfig构建器实例，用于配置缓存
        // 参数":user:cache:id:"是缓存的前缀名称
        QuickConfig idQc = QuickConfig.newBuilder(":user:cache:id:")
                // 2. 设置缓存类型为BOTH，表示同时使用本地缓存和分布式缓存
                .cacheType(CacheType.BOTH)
                // 3. 设置缓存过期时间为2小时
                .expire(Duration.ofHours(2))
                // 4. 启用本地缓存同步，确保本地缓存和分布式缓存数据一致
                .syncLocal(true)
                // 5. 构建QuickConfig对象
                .build();
        // 6. 使用配置创建或获取缓存实例
        // cacheManager是缓存管理器，负责管理所有缓存
        // idQc是上面配置的缓存配置
        idUserCache = cacheManager.getOrCreateCache(idQc);
    }



    @DistributeLock(keyExpression = "#telephone", scene = "USER_REGISTER")
    public UserOperatorResponse register(String telephone,String inviteCode){
        String defaultNickName;
        String randomString;
        //生成邀请码
        //生成默认昵称
        do {
            //创建一个6位数的字符串
            randomString = RandomUtil.randomString(6).toUpperCase();
            //创建一个默认的用户昵称
            defaultNickName = DEFAULT_NICK_NAME_PREFIX + randomString + telephone.substring(7,11);
            //如果创建的邀请码和用户昵称有一个存在于布隆或者是数据库中就重新创建，循环保证邀请码和用户昵称都不存在才能创建成功
        }while (nickNameExist(defaultNickName) && inviteCodeExist(inviteCode));

        //邀请人id初始值为null
        String inviterId = null;
        //判断传入的邀请码是否为空
        if (StringUtils.isNotBlank(inviteCode)){
            //如果不为空，从数据库查找找邀请码对应的邀请人id
            User inviter = userMapper.findByInviteCode(inviteCode);
            //判断邀请人是否存在
            if (inviter != null){
                //如果存在获取到邀请人id
                inviterId = inviter.getId().toString();
            }
        }
        //注册
        //将手机号、默认用户昵称、密码、初始邀请码、邀请人id,添加到数据库中
        User user = register(telephone, defaultNickName, DEFAULT_USER_PASSWORD, randomString, inviterId);
        //判断是否成功，如果失败就抛出IllegalArgumentException异常，错误码为用户操作失败
        Assert.notNull(user, UserErrorCode.USER_OPERATE_FAILED.getCode());

        //到这里表示添加成功
        //更新邀请排行
        //更新用户缓存
        //添加用户昵称到布隆中，方便后续判断创建昵称是否存在
        addNickName(defaultNickName);
        //添加用户邀请码到布隆中，方便后续判断邀请码是否存在
        addInviteCode(inviteCode);
        //更新邀请排行榜，并为邀请成功的人添加100积分作为报酬
        updateInviteRank(inviterId);
        //更新用户缓存信息,将用户信息加入到缓存当中
        updateUserCache(user.getId().toString(), user);
        //加入流水
        long streamResult = userOperateStreamService.insertStream(user, UserOperateTypeEnum.REGISTER);
        Assert.notNull(streamResult, () -> new BizException(RepoErrorCode.UPDATE_FAILED));

        UserOperatorResponse userOperatorResponse = new UserOperatorResponse();
        userOperatorResponse.setSuccess(true);

        return userOperatorResponse;
    }

    /**
     * 更新用户缓存
     * @param userId 用户id
     * @param user 用户信息
     */
    private void updateUserCache(String userId, User user){
        idUserCache.put(userId, user);
    }

    /**
     *  更新邀请人排行榜，为邀请人增加积分
     * @param inviterId
     */
    private void updateInviteRank(String inviterId){
        // 1. 检查传入的邀请人ID是否为null
        if (inviterId == null){
            // 2. 如果为null，直接返回，不执行后续操作
            return;
        }

        // 3. 从Redisson客户端获取一个分布式锁，锁的名称使用inviterId
        RLock rLock = redissonClient.getLock(inviterId);
        // 4. 获取锁，确保同一时间只有一个线程可以执行临界区代码
        rLock.lock();

        try{
            // 5. 从inviteRank有序分数集合中获取inviterId的当前分数
            Double score = inviteRank.getScore(inviterId);
            // 6. 检查分数是否为null（即该inviterId是否存在于集合中）
            if (score == null){
                // 7. 如果不存在，将分数初始化为0.0
                score = 0.0;
            }

            //8.获取最近一次上榜时间
            long currentTimeStamp = System.currentTimeMillis();
            //9.把上榜时间转成小数(时间戳13位，所以除以10000000000000能转成小数)，并且倒序排列（用1减），即上榜时间越早，分数越大（时间越晚，时间戳越大， 用1减一下， 就反过来了)
            double timePartScore = 1 - (double) currentTimeStamp / 10000000000000L;

            //当前积分保留整数，即移除上一次的小数位
            //当前积分加100，表示新邀请了一个用户
            //加上最近一次上榜时间的倒序小数位作为score

            // 8. 将当前分数加100.0，并将新的分数和inviterId添加到inviteRank集合中
            // 这表示该邀请人获得了100分的邀请奖励
            inviteRank.add(score.intValue() + 100.0 + timePartScore, inviterId);

        }finally {
            // 9. 在finally块中释放锁，确保锁一定会被释放
            rLock.unlock();
        }

    }

    /**
     * 将用户昵称加入到布隆过滤器中
     * @param nickName
     * @return
     */
    private boolean addNickName(String nickName){
        return this.nickNameBloomFilter != null && this.nickNameBloomFilter.add(nickName);
    }

    /**
     * 将用户邀请码加入到布隆过滤器中
     * @param inviteCode
     * @return
     */
    private boolean addInviteCode(String inviteCode){
        return this.inviteCodeBloomFilter != null && this.inviteCodeBloomFilter.add(inviteCode);
    }

    /**
     * 注册用户
     * @param telephone 手机号
     * @param nickName  用户昵称
     * @param password  密码
     * @param inviteCode 验证码
     * @param inviterId 邀请人id
     * @return
     */
    private User register(String telephone, String nickName, String password, String inviteCode, String inviterId){
        if (userMapper.findByTelephone(telephone) != null){
            throw new UserException(DUPLICATE_TELEPHONE_NUMBER);
        }

        User user = new User();
        user.register(telephone, nickName, password, inviteCode, inviterId);
        return save(user) ? user : null;
    }


    /**
     * 判断邀请码是否存在
     * @param inviteCode
     * @return
     */
    private boolean inviteCodeExist(String inviteCode){
        //从布隆中查到并且不为空，没有在布隆中获取为空，就表示不存在
        if (this.inviteCodeBloomFilter != null && this.inviteCodeBloomFilter.contains(inviteCode)){
            //从数据库中查找邀请码并且判断是否存在
            return userMapper.findByInviteCode(inviteCode) != null;
        }
        //直接返回false表示邀请码不存在
        return false;
    }

    /**
     * 判断用户昵称是否存在
     * @param defaultNickName
     * @return
     */
    private boolean nickNameExist(String defaultNickName){
        //从布隆中查到并且不为空，没有在布隆中获取为空，就表示不存在
        if (this.nickNameBloomFilter != null && this.nickNameBloomFilter.contains(defaultNickName)){
            //从数据库中查找用户昵称并且判断是否存在
            return userMapper.findByNickname(defaultNickName) != null;
        }
        //直接返回false表示邀请码不存在
        return false;
    }


    /**
     * 管理员注册
     * @param telephone 手机号
     * @param password 密码
     * @return
     */
    public UserOperatorResponse registerAdmin(String telephone, String password){

        User user = registerAdmin(telephone, telephone, password);
        Assert.notNull(user, UserErrorCode.USER_OPERATE_FAILED.getCode());
        idUserCache.put(user.getId().toString(), user);


        //加入流水
        long streamResult = userOperateStreamService.insertStream(user,UserOperateTypeEnum.REGISTER);
        Assert.notNull(streamResult, () -> new BizException(RepoErrorCode.UPDATE_FAILED));

        UserOperatorResponse userOperatorResponse = new UserOperatorResponse();
        userOperatorResponse.setSuccess(true);

        return userOperatorResponse;
    }

    /**
     * 管理员注册实现
     * @param telephone
     * @param nickName
     * @param password
     * @return
     */
    public User registerAdmin(String telephone, String nickName, String password){
        if (userMapper.findByTelephone(telephone) != null){
            throw new UserException(DUPLICATE_TELEPHONE_NUMBER);
        }

        User user = new User();
        user.registerAdmin(telephone, nickName, password);
        return save(user) ? user : null;
    }


    /**
     * 实名认证
     * @param userAuthRequest
     * @return
     */
    @CacheInvalidate(name = ":user:cache:id", key = "#userAuthRequest.userId")
    @Transactional(rollbackFor = Exception.class)
    public UserOperatorResponse auth(UserAuthRequest userAuthRequest){
        //创建用户操作响应对象
        UserOperatorResponse userOperatorResponse = new UserOperatorResponse();
        //通过id查询用户信息
        User user = userMapper.findById(userAuthRequest.getUserId());
        //如果为null表示用户不存在
        Assert.notNull(user, () -> new UserException(USER_NOT_EXIST));

        //如果用户有实名认证的权限，或者是上链成功的权限，表示用户已经实名认证过
        if (user.getState() == UserStateEnum.AUTH || user.getState() == UserStateEnum.ACTIVE){
            userOperatorResponse.setSuccess(true);
            userOperatorResponse.setUser(UserConvertor.INSTANCE.mapToVo(user));

            return userOperatorResponse;
        }
        //到这里表示没有实名，调用第三方接口进行实名认证
        //如果用户是刚创建的时不允许实名认证的
        Assert.isTrue(user.getState() == UserStateEnum.INIT, () -> new UserException(USER_STATUS_IS_NOT_INIT) );
        //调用认证service验证实名是否成功，如果成功则修改用户状态为认证成功，否则抛出异常
        Assert.isTrue(authService.checkAuth(userAuthRequest.getRealName(),userAuthRequest.getIdCard()), () -> new UserException(USER_AUTH_FAIL));
        user.auth(userAuthRequest.getRealName(), userAuthRequest.getIdCard());
        boolean result = updateById(user);
        if (result){
            //加入流水
            long streamResult = userOperateStreamService.insertStream(user, UserOperateTypeEnum.AUTH);
            Assert.notNull(streamResult, () -> new BizException(RepoErrorCode.UPDATE_FAILED));
            userOperatorResponse.setSuccess(true);
            userOperatorResponse.setUser(UserConvertor.INSTANCE.mapToVo(user));
        }else {
            userOperatorResponse.setSuccess(false);
            userOperatorResponse.setResponseCode(USER_OPERATE_FAILED.getCode());
            userOperatorResponse.setResponseMessage(USER_OPERATE_FAILED.getMessage());
        }

        return userOperatorResponse;
    }


    /**
     * 用户激活
     * @param userActiveRequest
     * @return
     */
    @CacheInvalidate(name = ":user:cache:id", key = "#userActiveRequest.userId")
    @Transactional(rollbackFor = Exception.class)
    public UserOperatorResponse active(UserActiveRequest userActiveRequest){
        UserOperatorResponse userOperatorResponse = new UserOperatorResponse();
        User user = userMapper.findById(userActiveRequest.getUserId());
        Assert.notNull(user.getState() == UserStateEnum.AUTH, () -> new UserException(USER_STATUS_IS_NOT_AUTH));
        user.active(userActiveRequest.getBlockChainUrl(), userActiveRequest.getBlockChainPlatform());
        boolean result = updateById(user);
        if (result){
            //加入流水
            long streamResult = userOperateStreamService.insertStream(user, UserOperateTypeEnum.ACTIVE);
            Assert.notNull(streamResult, () -> new BizException(RepoErrorCode.UPDATE_FAILED));
            userOperatorResponse.setSuccess(true);
        }else{
            userOperatorResponse.setSuccess(false);
            userOperatorResponse.setResponseMessage(USER_OPERATE_FAILED.getMessage());
            userOperatorResponse.setResponseCode(USER_OPERATE_FAILED.getCode());
        }
        return userOperatorResponse;
    }

    /**
     * 冻结
     * @param userId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public UserOperatorResponse freeze(Long userId){
        //查询用户
        UserOperatorResponse userOperatorResponse = new UserOperatorResponse();
        User user = userMapper.findById(userId);
        Assert.notNull(user, () -> new UserException(USER_NOT_EXIST));
        Assert.isTrue(user.getState() == UserStateEnum.ACTIVE, () -> new UserException(USER_STATUS_IS_NOT_ACTIVE));


        //第一次删除缓存
        idUserCache.remove(user.getId().toString());

        if (user.getState() == UserStateEnum.FROZEN){
            userOperatorResponse.setSuccess(true);
            return userOperatorResponse;
        }
        //更新数据库
        user.setState(UserStateEnum.FROZEN);
        boolean updateResult = updateById(user);
        Assert.isTrue(updateResult, () -> new BizException(RepoErrorCode.UPDATE_FAILED));

        //加入流水
        long result = userOperateStreamService.insertStream(user, UserOperateTypeEnum.FREEZE);
        Assert.notNull(result, () -> new BizException(RepoErrorCode.UPDATE_FAILED));
        //第二次删除缓存
        userCacheDelayDeleteService.delayedCacheDelete(idUserCache, user);

        userOperatorResponse.setSuccess(true);
        return userOperatorResponse;
    }

    /**
     * 解冻
     * @param userId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public UserOperatorResponse unfreeze(Long userId){
        UserOperatorResponse userOperatorResponse = new UserOperatorResponse();
        User user = userMapper.findById(userId);
        Assert.notNull(user, () -> new UserException(USER_NOT_EXIST));

        //第一次删除缓存
        idUserCache.remove(user.getId().toString());

        if (user.getState() == UserStateEnum.ACTIVE){
            userOperatorResponse.setSuccess(true);
            return userOperatorResponse;
        }

        user.setState(UserStateEnum.ACTIVE);
        //更新数据库
        boolean updateResult = updateById(user);
        Assert.isTrue(updateResult, () -> new BizException(RepoErrorCode.UPDATE_FAILED));
        //加入流水
        long result = userOperateStreamService.insertStream(user, UserOperateTypeEnum.UNFREEZE);

        Assert.notNull(result, () -> new BizException(RepoErrorCode.UPDATE_FAILED));

        //第二次删除缓存
        userCacheDelayDeleteService.delayedCacheDelete(idUserCache, user);

        userOperatorResponse.setSuccess(true);
        return userOperatorResponse;
    }

    /**
     * 分页查询用户信息
     * @param keyWord
     * @param state
     * @param currentPage
     * @param pageSize
     * @return
     */
    public PageResponse<User> pageQueryByState(String keyWord, String state, int currentPage, int pageSize){

        Page<User> page = new Page<>(currentPage, pageSize);
        Page<User> userPage = this.page(page, new LambdaQueryWrapper<User>()
                .eq(User::getState, state)
                .like(StringUtils.isNotBlank(keyWord), User::getTelephone, keyWord)
                .orderByAsc(User::getGmtCreate)
        );
        return PageResponse.of(userPage.getRecords(), (int) userPage.getTotal(), pageSize, currentPage);
    }

    //#############################查询用户##############################

    /**
     * 通过用户id查询用户信息
     * @param userId
     * @return
     */
    @Cached(name = ":user:cache:id:", cacheType = CacheType.BOTH, key = "#userId", cacheNullValue = true)
    @CacheRefresh(refresh = 60, timeUnit = TimeUnit.MINUTES)
    public User findById(Long userId){

        return userMapper.findById(userId);
    }

    /**
     * 通过手机号查询用户信息
     * @param telephone 手机号
     * @return
     */
    public User findByTelephone(String telephone){
        return userMapper.findByTelephone(telephone);
    }

    /**
     * 通过手机号和密码查询用户信息
     * @param telephone 手机号
     * @param password 用户密码
     * @return
     */
    public User findByTelephoneAndPass(String telephone, String password){
        return userMapper.findByTelephoneAndPass(telephone, DigestUtil.md5Hex(password));
    }

    //######################################修改用户######################################

    /**
     * 修改用户信息
     * @param userModifyRequest 用户信息参数
     * @return
     */
    @CacheInvalidate(name = ":user:cache:id:", key = "#userModifyRequest.userId")
    @Transactional(rollbackFor = Exception.class)
    public UserOperatorResponse modify(UserModifyRequest userModifyRequest){
        //先创建一个user响应对象
        UserOperatorResponse userOperatorResponse = new UserOperatorResponse();
        //从数据库中查询出用户信息
        User user = userMapper.findById(userModifyRequest.getUserId());
        //判断用户是否存在，如果不存在抛出异常
        Assert.notNull(user, () -> new UserException(USER_NOT_EXIST));
        //判断用户状态是否可以修改信息
        Assert.isTrue(user.canModifyInfo(), () -> new UserException(USER_STATUS_CANT_OPERATE));
        //判断用户昵称是否为空，并且判断用户昵称是否已经有了
        if (StringUtils.isNotBlank(userModifyRequest.getNickName()) && nickNameExist(userModifyRequest.getNickName())){
            throw new UserException(NICK_NAME_EXIST);
        }

        //将接收到的参数拷贝至user对象中
        BeanUtils.copyProperties(userModifyRequest, user);

        //判断用户密码是否修改，如果修改就将密码加密后存入到user表中
        if (StringUtils.isNotBlank(userModifyRequest.getPassword())){
            user.setPasswordHash(DigestUtil.md5Hex(userModifyRequest.getPassword()));
        }

        if (updateById(user)){
            //如果修改成功，将用户操作流水存入数据库中
            long streamResult = userOperateStreamService.insertStream(user, UserOperateTypeEnum.MODIFY);
            Assert.notNull(streamResult, () -> new BizException(RepoErrorCode.UPDATE_FAILED));
            //修改成功，将名称加入到布隆过滤器中
            addNickName(userModifyRequest.getNickName());
            userOperatorResponse.setSuccess(true);

            return userOperatorResponse;
        }
        userOperatorResponse.setSuccess(false);
        userOperatorResponse.setResponseCode(USER_OPERATE_FAILED.getCode());
        userOperatorResponse.setResponseMessage(USER_OPERATE_FAILED.getMessage());

        return userOperatorResponse;
    }

    /**
     * 获取排名前N的邀请信息
     *
     * @param topN 需要获取的排名前N的数量
     * @return 返回一个包含排名前N的InviteRankInfo对象列表
     */
    public List<InviteRankInfo> getTopN(Integer topN){
        // 从邀请排名中获取前N个排名信息，逆序排列以便获取排名最靠前的记录
        Collection<ScoredEntry<String>> rankInfos = inviteRank.entryRangeReversed(0, topN -1);

        // 初始化邀请排名信息列表
        List<InviteRankInfo> inviteRankInfos = new ArrayList<>();

        // 检查排名信息是否非空
        if (rankInfos != null){
            // 遍历排名信息集合
            for (ScoredEntry<String> rankInfo : rankInfos){
                // 初始化邀请排名信息对象
                InviteRankInfo inviteRankInfo = new InviteRankInfo();
                // 获取用户ID
                String userId = rankInfo.getValue();
                // 检查用户ID是否非空且不为空白字符
                if (StringUtils.isNotBlank(userId)){
                    // 根据用户ID查找用户
                    User user = findById(Long.valueOf(userId));

                    // 检查用户是否非空
                    if (user != null){
                        // 设置邀请码
                        inviteRankInfo.setInviteCode(user.getInviteCode());
                        // 设置昵称
                        inviteRankInfo.setNickName(user.getNickName());
                        // 设置邀请次数
                        inviteRankInfo.setInviteScore(rankInfo.getScore().intValue());
                        // 将邀请排名信息添加到列表中
                        inviteRankInfos.add(inviteRankInfo);
                    }
                }
            }
        }
        // 返回邀请排名信息列表
        return inviteRankInfos;
    }

    /**
     * 用于在Bean属性设置完成后执行初始化操作。下面是
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {

        // 1. 从Redisson客户端获取名为"nickName"的布隆过滤器实例
        this.nickNameBloomFilter = redissonClient.getBloomFilter("nickName");

        // 2. 检查nickNameBloomFilter是否存在且未被初始化
        if (nickNameBloomFilter != null && !nickNameBloomFilter.isExists()){
            // 3. 如果不存在，则初始化布隆过滤器
            // 参数说明：
            // 100000L - 预期要插入的元素数量
            // 0.01 - 可接受的误判率(1%)
            this.nickNameBloomFilter.tryInit(100000L, 0.01);
        }

        // 4. 从Redisson客户端获取名为"inviteCode"的布隆过滤器实例
        this.inviteCodeBloomFilter = redissonClient.getBloomFilter("inviteCode");
        // 5. 检查inviteCodeBloomFilter是否存在且未被初始化
        if (inviteCodeBloomFilter != null && !inviteCodeBloomFilter.isExists()){
            // 6. 如果不存在，则初始化布隆过滤器
            // 使用与上面相同的参数
            this.inviteCodeBloomFilter.tryInit(100000L, 0.01);
        }

        // 7. 从Redisson客户端获取名为"inviteRank"的有序分数集合(Sorted Set)实例
        this.inviteRank = redissonClient.getScoredSortedSet("inviteRank");
    }

    public Integer getInviteRank(String userId) {
        Integer rank = inviteRank.revRank(userId);
        if (rank != null){
            return rank + 1;
        }
        return null;
    }

    /**
     * 根据邀请者ID获取用户列表
     *
     * @param inviterId 邀请者的ID，用于查询被邀请的用户
     * @param currentPage 当前页码，用于分页查询
     * @param pageSize 每页记录数，用于分页查询
     * @return 返回一个包含用户列表的PageResponse对象
     *
     * 此方法通过构建查询条件和分页参数，从数据库中查询被邀请的用户信息
     * 它只选择用户的昵称和创建时间字段，并根据创建时间降序排序结果
     */
    public PageResponse<User> getUsersByInviterId(String inviterId, int currentPage, int pageSize) {
        // 创建分页对象，传入当前页码和每页大小
        Page<User> page = new Page<>(currentPage, pageSize);

        // 创建查询条件包装器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // 选择要查询的字段：昵称和创建时间
        wrapper.select("nick_name", "gmt_create");
        // 添加查询条件：邀请者ID等于传入的参数inviterId
        wrapper.eq("inviter_id", inviterId);

        // 设置排序条件：根据创建时间降序排序
        wrapper.orderBy(true, false, "gmt_create");

        // 执行分页查询
        Page<User> userPage = this.page(page, wrapper);

        // 将查询结果封装到PageResponse对象中并返回
        return PageResponse.of(userPage.getRecords(), (int) userPage.getTotal(), pageSize, currentPage);
    }
}
